/* 
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using IndexInput = Lucene.Net.Store.IndexInput;

namespace Lucene.Net.Index
{
    internal sealed class SegmentTermEnum : TermEnum, System.ICloneable
    {
        private IndexInput input;
        internal FieldInfos fieldInfos;
        internal long size;
        internal long position = - 1;
        
        private TermBuffer termBuffer = new TermBuffer();
        private TermBuffer prevBuffer = new TermBuffer();
        private TermBuffer scanBuffer = new TermBuffer(); // used for scanning
        
        private TermInfo termInfo = new TermInfo();
        
        private readonly int format;
        private readonly bool isIndex = false;
        internal long indexPointer = 0;
        internal int indexInterval;
        internal int skipInterval;
        internal int maxSkipLevels;
        private readonly int formatM1SkipInterval;
        
        internal SegmentTermEnum(IndexInput i, FieldInfos fis, bool isi)
        {
            input = i;
            fieldInfos = fis;
            isIndex = isi;
            maxSkipLevels = 1; // use single-level skip lists for formats > -3 
            
            int firstInt = input.ReadInt();
            if (firstInt >= 0)
            {
                // original-format file, without explicit format version number
                format = 0;
                size = firstInt;
                
                // back-compatible settings
                indexInterval = 128;
                skipInterval = System.Int32.MaxValue; // switch off skipTo optimization
            }
            else
            {
                // we have a format version number
                format = firstInt;
                
                // check that it is a format we can understand
                if (format < TermInfosWriter.FORMAT_CURRENT)
                    throw new CorruptIndexException("Unknown format version:" + format + " expected " + TermInfosWriter.FORMAT_CURRENT + " or higher");
                
                size = input.ReadLong(); // read the size
                
                if (format == - 1)
                {
                    if (!isIndex)
                    {
                        indexInterval = input.ReadInt();
                        formatM1SkipInterval = input.ReadInt();
                    }
                    // switch off skipTo optimization for file format prior to 1.4rc2 in order to avoid a bug in 
                    // skipTo implementation of these versions
                    skipInterval = System.Int32.MaxValue;
                }
                else
                {
                    indexInterval = input.ReadInt();
                    skipInterval = input.ReadInt();
                    if (format <= TermInfosWriter.FORMAT)
                    {
                        // this new format introduces multi-level skipping
                        maxSkipLevels = input.ReadInt();
                    }
                }
                System.Diagnostics.Debug.Assert(indexInterval > 0, "indexInterval=" + indexInterval + " is negative; must be > 0");
                System.Diagnostics.Debug.Assert(skipInterval > 0, "skipInterval=" + skipInterval + " is negative; must be > 0");
            }
            if (format > TermInfosWriter.FORMAT_VERSION_UTF8_LENGTH_IN_BYTES)
            {
                termBuffer.SetPreUTF8Strings();
                scanBuffer.SetPreUTF8Strings();
                prevBuffer.SetPreUTF8Strings();
            }
        }
        
        public System.Object Clone()
        {
            SegmentTermEnum clone = null;
            try
            {
                clone = (SegmentTermEnum) base.MemberwiseClone();
            }
            catch (System.Exception)
            {
            }
            
            clone.input = (IndexInput) input.Clone();
            clone.termInfo = new TermInfo(termInfo);
            
            clone.termBuffer = (TermBuffer) termBuffer.Clone();
            clone.prevBuffer = (TermBuffer) prevBuffer.Clone();
            clone.scanBuffer = new TermBuffer();
            
            return clone;
        }
        
        internal void  Seek(long pointer, long p, Term t, TermInfo ti)
        {
            input.Seek(pointer);
            position = p;
            termBuffer.Set(t);
            prevBuffer.Reset();
            termInfo.Set(ti);
        }
        
        /// <summary>Increments the enumeration to the next element.  True if one exists.</summary>
        public override bool Next()
        {
            if (position++ >= size - 1)
            {
                prevBuffer.Set(termBuffer);
                termBuffer.Reset();
                return false;
            }
            
            prevBuffer.Set(termBuffer);
            termBuffer.Read(input, fieldInfos);
            
            termInfo.docFreq = input.ReadVInt(); // read doc freq
            termInfo.freqPointer += input.ReadVLong(); // read freq pointer
            termInfo.proxPointer += input.ReadVLong(); // read prox pointer
            
            if (format == - 1)
            {
                //  just read skipOffset in order to increment  file pointer; 
                // value is never used since skipTo is switched off
                if (!isIndex)
                {
                    if (termInfo.docFreq > formatM1SkipInterval)
                    {
                        termInfo.skipOffset = input.ReadVInt();
                    }
                }
            }
            else
            {
                if (termInfo.docFreq >= skipInterval)
                    termInfo.skipOffset = input.ReadVInt();
            }
            
            if (isIndex)
                indexPointer += input.ReadVLong(); // read index pointer
            
            return true;
        }
        
        /// <summary>Optimized scan, without allocating new terms. 
        /// Return number of invocations to next(). 
        /// </summary>
        internal int ScanTo(Term term)
        {
            scanBuffer.Set(term);
            int count = 0;
            while (scanBuffer.CompareTo(termBuffer) > 0 && Next())
            {
                count++;
            }
            return count;
        }

        /// <summary>Returns the current Term in the enumeration.
        /// Initially invalid, valid after next() called for the first time.
        /// </summary>
        public override Term Term
        {
            get { return termBuffer.ToTerm(); }
        }

        /// <summary>Returns the previous Term enumerated. Initially null.</summary>
        public /*internal*/ Term Prev()
        {
            return prevBuffer.ToTerm();
        }
        
        /// <summary>Returns the current TermInfo in the enumeration.
        /// Initially invalid, valid after next() called for the first time.
        /// </summary>
        internal TermInfo TermInfo()
        {
            return new TermInfo(termInfo);
        }
        
        /// <summary>Sets the argument to the current TermInfo in the enumeration.
        /// Initially invalid, valid after next() called for the first time.
        /// </summary>
        internal void  TermInfo(TermInfo ti)
        {
            ti.Set(termInfo);
        }
        
        /// <summary>Returns the docFreq from the current TermInfo in the enumeration.
        /// Initially invalid, valid after next() called for the first time.
        /// </summary>
        public override int DocFreq()
        {
            return termInfo.docFreq;
        }
        
        /* Returns the freqPointer from the current TermInfo in the enumeration.
        Initially invalid, valid after next() called for the first time.*/
        internal long FreqPointer()
        {
            return termInfo.freqPointer;
        }
        
        /* Returns the proxPointer from the current TermInfo in the enumeration.
        Initially invalid, valid after next() called for the first time.*/
        internal long ProxPointer()
        {
            return termInfo.proxPointer;
        }
        
        /// <summary>Closes the enumeration to further activity, freeing resources. </summary>
        protected override void Dispose(bool disposing)
        {
            input.Dispose();
        }
    }
}